import coin.proto.coin_market_enums_pb2 as coin_enum
from coin.exchange.base.order_gateway_log_builder import (prepare_builder, BaseOrderEventBuilder)
from coin.exchange.base.order_gateway_logger import OrderGatewayLogProvider
from coin.exchange.okex_futures.types import OkexFuturesOrderSide
from coin.proto.coin_order_gateway_pb2 import OrderEvent
import coin.proto.coin_order_enums_pb2 as coin_order


def _to_proto_order_side(order_side: OkexFuturesOrderSide):
  if order_side == OkexFuturesOrderSide.BUY_OPEN:
    order_side = coin_order.BUY_OPEN_ORDER
  elif order_side == OkexFuturesOrderSide.BUY_CLOSE:
    order_side = coin_order.BUY_CLOSE_ORDER
  elif order_side == OkexFuturesOrderSide.SELL_OPEN:
    order_side = coin_order.SELL_OPEN_ORDER
  elif order_side == OkexFuturesOrderSide.SELL_CLOSE:
    order_side = coin_order.SELL_CLOSE_ORDER
  else:
    raise ValueError(order_side)
  return order_side


class OrderEventBuilder(BaseOrderEventBuilder):
  def set_order(self, order):
    internal_order_id = order.order_id
    external_order_id = order.internal.exchange_order_id
    order_price = order.price
    order_qty = order.qty
    order_side = _to_proto_order_side(order.order_side)
    order_type = coin_order.DAY_LIMIT_ORDER

    self.oe.MergeFrom(
        OrderEvent(internal_order_id=internal_order_id,
                   external_order_id=str(external_order_id),
                   order_price=order_price,
                   order_qty=order_qty,
                   order_side=order_side,
                   order_type=order_type))


class OkexFuturesOrderGatewayLogProvider(OrderGatewayLogProvider):
  exchange_type = coin_enum.Okex

  def gen_order_event_and_log(self, status, order, timestamp, order_event_type=None, fill_qty=None):
    if not order_event_type:
      # status: https://www.okex.com/ws_api.html
      #   -1: cancelled
      #    0: pending
      #    1: partially filled
      #    2: fully filled
      #    4: cancel request in process
      if status == (0, 1, 2):
        # Already taken care of, skip.
        return
      elif status in (3, 4):
        order_event_type = OrderEvent.CANCEL_ACCEPTED
      elif status == -1:
        order_event_type = OrderEvent.CANCEL_CONFIRMED
      else:
        raise ValueError(status)

    builder = prepare_builder(OrderEventBuilder,
                              None,
                              timestamp,
                              self.get_next_order_event_id(),
                              self.exchange_type,
                              order.product.symbol)
    builder.set_order(order)

    if order_event_type == OrderEvent.ORDER_ACCEPTED:
      order_state = coin_order.WORKING_ORDER
    elif order_event_type == OrderEvent.ORDER_FILLED:
      assert fill_qty is not None
      fill_price = order.internal.fill.value.price_avg_fill
      if status == 1:
        order_state = coin_order.WORKING_ORDER
      elif status == 2:
        order_state = coin_order.DEAD_ORDER
      else:
        raise ValueError(status)
      builder.set_fill(fill_price, fill_qty)
    elif order_event_type == OrderEvent.CANCEL_ACCEPTED:
      order_state = coin_order.WORKING_ORDER
    elif order_event_type == OrderEvent.CANCEL_CONFIRMED:
      order_state = coin_order.DEAD_ORDER
    else:
      raise NotImplementedError()

    builder.set_state(order_event_type=order_event_type,
                      order_state=order_state,
                      order_event_source=OrderEvent.EXCHANGE)
    self.write_order_event(builder.oe)

  def gen_order_request_and_order_event_and_log(self,
                                                symbol,
                                                order_price,
                                                order_qty,
                                                order_side,
                                                order_type,
                                                internal_order_id,
                                                timestamp):
    order_side = _to_proto_order_side(order_side)
    super().gen_order_request_and_order_event_and_log(symbol,
                                                      order_price,
                                                      order_qty,
                                                      order_side,
                                                      order_type,
                                                      internal_order_id,
                                                      timestamp)
